#include "public_def.h"

#include "arrayctrl.h"


//Defination of object container for dynamic array
typedef struct AC_Object
{
	void * data;
	struct AC_Object *prev, *next;
} AC_Object_t;

//Defination of header of dynamic array
typedef struct AC_Header
{
	int curIdx, count;
	struct AC_Object *begin, * end, * cur;
	OnObjectDestroyHandle OnDestroyCallBack;
} AC_Header_t;

//Create dynamic array
//
//Parameter:
//	destroyObjectCallBack 	Callback handle for removing object from the list
//
//Return:
//	Handle of succ created array
//
ACHANDLE AC_Create(OnObjectDestroyHandle destroyObjectCallBack)
{
	AC_Header_t * al = (AC_Header_t *)malloc(sizeof(AC_Header_t));
	if (al)
	{
		al->curIdx = -1;
		al->count = 0;
		al->begin = al->end = al->cur = NULL;
		al->OnDestroyCallBack = destroyObjectCallBack;

		return (ACHANDLE)al;
	}
	else
	{
		return 0;
	}
}

//Add an object to the list
//
//Parameter:
//	handle		handle of dynamic array
//	object		object reference
//Return:
//	Object index in current list
//
int AC_Add(ACHANDLE handle, void * object)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if (al)
		{
			AC_Object_t * obj = (AC_Object_t *)malloc(sizeof(AC_Object_t));
			obj->data = object;
			obj->prev = al->end;
			obj->next = NULL;
			if (al->begin == NULL)
			{
				al->begin = obj;
			}

			if (al->end)
			{
				al->end->next = obj;
			}
			al->end = obj;
			al->count ++;
			al->curIdx = al->count - 1;
			al->cur = obj;
			return al->curIdx;
		}
	}
	return -1;
}

//Get the number of elements
//
//Parameter:
//	handle		handle of dynamic array
//Return:
//	Number of elements
//
int AC_Count(ACHANDLE handle)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if (al)
		{
			return al->count;
		}
	}
	return 0;
}

//Private function
//
//Parameter:
//
//Return:
//
AC_Object_t * _AC_Prev(ACHANDLE handle)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if (al)
		{
			if (al->curIdx > 0)
			{
				al->curIdx --;
				al->cur = al->cur->prev;
				return al->cur;
			}
		}
	}
	return NULL;
}

//Get the previous object
//
//Parameter:
//	handle		handle of dynamic array
//Return:
//	object reference
//
void * AC_Prev(ACHANDLE handle)
{
	AC_Object_t * obj = _AC_Prev(handle);
	return ((obj) ? obj->data : NULL);
}


//Private function
//
//Parameter:
//
//Return:
//
AC_Object_t * _AC_Next(ACHANDLE handle)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if (al)
		{
			if (al->curIdx < al->count - 1)
			{
				al->curIdx ++;
				al->cur = al->cur->next;
				return al->cur;
			}
		}
	}
	return NULL;
}

//Get the next element
//
//Parameter:
//	handle		handle of dynamic array
//Return:
//	object reference
//
void * AC_Next(ACHANDLE handle)
{
	AC_Object_t * obj = _AC_Next(handle);
	return ((obj) ? obj->data : NULL);
}

//Private function
//
//Parameter:
//
//Return:
//
AC_Object_t * _AC_Get(ACHANDLE handle, int iIndex)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if ((al->count >= iIndex) && (iIndex >= 0))
		{
			//just at current position
			if (iIndex == al->curIdx)
			{
				return al->cur;
			}
			else if (iIndex == 0)
			{
				al->curIdx = iIndex;
				return al->cur = al->begin;
			}
			else if (iIndex == al->count - 1)
			{
				al->curIdx = iIndex;
				return al->cur = al->end;
			}
			int inc = (iIndex > al->curIdx)?(1) : (-1);
			AC_Object_t * obj;

			while ((al->curIdx >= 0) && (al->curIdx < al->count))
			{
				if (inc > 0)
				{
					obj = _AC_Next(handle);
				}
				else
				{
					obj = _AC_Prev(handle);
				}

				if (obj == NULL)
				{
					break;
				}
				if (iIndex == al->curIdx)
				{
					return al->cur;
				}
			}
		}
	}
	return NULL;
}

//Private function
//
//Parameter:
//
//Return:
//
AC_Object_t * _AC_GetObject(ACHANDLE handle, void * obj)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		int i = 0;
		for(i = 0; i < al->count; i ++)
		{
			AC_Object_t * obj1 = _AC_Get(handle, i);
			if (obj1)
			{
				if (obj1->data == obj)
				{
					return obj1;
				}
			}
		}
	}
	return NULL;
}

//Get object by index
//
//Parameter:
//	handle		handle of dynamic array
//	iIndex		object index
//
//Return:
//	object reference
//
void *AC_Get(ACHANDLE handle, int iIndex)
{
	AC_Object_t * obj = _AC_Get(handle, iIndex);
	return ((obj) ? obj->data : NULL);
}

//Clear dynamic array
//
//Parameter:
//	handle		handle of dynamic array
//
//Return:
//	None
//
void AC_Clear(ACHANDLE handle)
{
	while (AC_Count(handle))
	{
		AC_Remove(handle, 0);
	}
}

//Insert object by index
//
//Parameter:
//	handle		handle of dynamic array
//	iIndex		object index
//
//Return:
//	current index
//
int AC_Insert(ACHANDLE handle, void * object, int iIndex)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		AC_Object_t * obj = _AC_Get(handle, iIndex);
		if (obj)
		{
			AC_Object_t * newobj = (AC_Object_t *)malloc(sizeof(AC_Object_t));
			newobj->data = object;
			newobj->prev = obj->prev;
			newobj->next = obj;
			if (newobj->prev)
			{
				newobj->prev->next = newobj;
			}

			if (newobj->next)
			{
				newobj->next->prev = newobj;
			}

			al->count ++;
			al->cur = newobj;

			if (obj == al->begin)
			{
				al->begin = newobj;
			}
			return al->curIdx;

		}
	}
	return -1;
}

//Remove object by index
//
//Parameter:
//	handle		handle of dynamic array
//	iIndex		object index
//
//Return:
//	None
//
void AC_Remove(ACHANDLE handle, int iIndex)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;

		AC_Object_t * obj = _AC_Get(handle, iIndex);
		if (obj)
		{
			if ((obj == al->begin) || (obj == al->end))
			{
				if (obj == al->begin)
				{
					al->begin = obj->next;
					if (al->begin)
					{
						al->begin->prev = NULL;
					}
					al->cur = al->begin;
				}
				if (obj == al->end)
				{
					al->end = obj->prev;
					if (al->end)
					{
						al->end->next = NULL;
					}
					al->cur = al->end;
					al->curIdx--;
				}
			}
			else
			{
				obj->prev->next = obj->next;
				obj->next->prev = obj->prev;
				al->cur = obj->next;
			}
			al->count --;
			if (al->OnDestroyCallBack)
			{
				al->OnDestroyCallBack(obj->data);
				obj->data = NULL;
			}
			free(obj);
		}
	}
}

//Remove object by object reference
//
//Parameter:
//	handle		handle of dynamic array
//	object 		object reference
//
//Return:
//	None
//
void AC_RemoveObject(ACHANDLE handle, void * object)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;

		AC_Object_t * obj = _AC_GetObject(handle, object);
		if (obj)
		{
			if ((obj == al->begin) || (obj == al->end))
			{
				if (obj == al->begin)
				{
					al->begin = obj->next;
					if (al->begin)
					{
						al->begin->prev = NULL;
					}
					al->cur = al->begin;
				}
				if (obj == al->end)
				{
					al->end = obj->prev;
					if (al->end)
					{
						al->end->next = NULL;
					}
					al->cur = al->end;
					al->curIdx--;
				}
			}
			else
			{
				obj->prev->next = obj->next;
				obj->next->prev = obj->prev;
				al->cur = obj->next;
			}
			al->count --;
			if (al->OnDestroyCallBack)
			{
				al->OnDestroyCallBack(obj->data);
                obj->data = NULL;
			}
			free(obj);
		}
	}
}

//Destroy dynamic array
//
//Parameter:
//	handle		handle of dynamic array
//
//Return:
//	None
//
void AC_Destroy(ACHANDLE *handle)
{
	if (*handle)
	{
		AC_Header_t * al = (AC_Header_t *)(*handle);
		if (al)
		{
			while (al->count > 0)
			{
				AC_Remove(*handle, 0);
			}
			free(al);
			*handle = 0;
		}
	}
}

//Get the current index in array
//
//Parameter:
//	handle		handle of dynamic array
//
//Return:
//	object index
//
int AC_Pos(ACHANDLE handle)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if (al)
		{
			return al->curIdx;
		}
	}
	return -1;
}

//Get the first element
//
//Parameter:
//	handle		handle of dynamic array
//
//Return:
//	object reference
//
void *AC_First(ACHANDLE handle)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if (al)
		{
			al->curIdx = 0;
			al->cur = al->begin;
			return al->cur->data;
		}
	}
	return NULL;
}

//Get the last element
//
//Parameter:
//	handle		handle of dynamic array
//
//Return:
//	object reference
//
void *AC_Last(ACHANDLE handle)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if (al)
		{
			al->curIdx = al->count - 1;
			al->cur = al->end;
			return al->cur->data;
		}
	}
	return NULL;
}

//Determine whether the cursor is at end of list
//
//Parameter:
//	handle		handle of dynamic array
//
//Return:
//	true / false
//
bool AC_IsLast(ACHANDLE handle)
{
	if (handle)
	{
		AC_Header_t * al = (AC_Header_t *)handle;
		if (al)
		{
			return ((al->cur == al->end) ? true : false);
		}
	}
	return false;
}
